using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Collections;
using CustomControls.ContextMenuScope;

namespace CustomControls
{
    namespace Grid
    {
        [ToolboxData("<{0}:xGrid runat=server></{0}:xGrid>")]
        public partial class xGrid : GridView
        {
            #region Local members
            private TableItemStyle filterStyle;
            private TextBox searchBox;
            private ArrayList cachedSelectedIndices;

            private bool isFiltered = false;
            private bool autoGenerateCheckBoxColumn = false;
            private int checkBoxColumnIndex = 0;
            private bool enableRowClick = false;
            private bool enableRowDoubleClick = false;
            private bool enableHeightResolver = true;

            // GridRow used for custom header
            private GridViewRow gvRowSearch = 
                new GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);
            #endregion

            #region Constant members
            private const string GRIDVIEW_JS= "CustomControls.GridViewJS.js";
            #endregion

            #region Height Resolver
            /// <summary>
            /// Gets/Sets whether the GridView Height Resolver is enabled or disabled
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"), DefaultValue(false), Description("Gets/Sets whether the GridView Height Resolver is enabled or disabled")]
            public bool EnableHeightResolver
            {
                get
                {
                    return this.enableHeightResolver;
                }
                set
                {
                    this.enableHeightResolver = value;
                }
            }
            #endregion

            #region Sorting image
            /// <summary>
            /// Gets/Sets the path to the Ascending Image used in the GridView Header
            /// </summary>
            [Category("Extended GridView Properties")]
            [Description("Gets/Sets the path to the Ascending Image used in the GridView Header")]
            [Editor(typeof(System.Web.UI.Design.ImageUrlEditor), typeof(System.Drawing.Design.UITypeEditor))]
            public string AscImage
            {
                get
                {
                    string ascImage = string.Empty;
                    object obj = ViewState["AscImage"];
                    if (obj != null)
                    {
                        ascImage = obj.ToString();
                    }
                    return ascImage;
                }
                set
                {
                    ViewState["AscImage"] = value;
                }
            }
            
            /// <summary>
            /// Gets/Sets the path to the Descending Image used in the GridView Header
            /// </summary>
            [Category("Extended GridView Properties")]
            [Description("Gets/Sets the path to the Descending Image used in the GridView Header")]
            [Editor(typeof(System.Web.UI.Design.ImageUrlEditor), typeof(System.Drawing.Design.UITypeEditor))]
            public string DescImage
            {
                get
                {
                    string descImage = string.Empty;
                    object obj = ViewState["DescImage"];
                    if (obj != null)
                    {
                        descImage = obj.ToString();
                    }
                    return descImage;
                }
                set
                {
                    ViewState["DescImage"] = value;
                }
            }
            #endregion

            #region Checkbox Column
            
            /// <summary>
            /// Get/Sets the AutoGenerateCheckBox Column Property
            /// </summary>
            [Category("Extended GridView Properties")]
            [Description("Get/Sets the AutoGenerateCheckBox Column Property")]
            [DefaultValue(false)]
            public bool AutoGenerateCheckBoxColumn
            {
                get
                {
                    return this.autoGenerateCheckBoxColumn;
                }
                set {
                    this.autoGenerateCheckBoxColumn = value;
                }
            }

            /// <summary>
            /// Gets/Sets CheckBox Column Index
            /// </summary>
            [Category("Extended GridView Properties")]
            [Description("Gets/Sets CheckBox Column Index")]
            [DefaultValue(0)]
            public int CheckBoxColumnIndex
            {
                get
                {
                    return this.checkBoxColumnIndex;
                }
                set
                {
                    this.checkBoxColumnIndex = (value < 0 ? 0 : value);
                }
            }

            // Actual Column Number 
            internal virtual int ActualColumns
            {
                get
                {
                    if (!AutoGenerateCheckBoxColumn)
                    {
                        return this.Columns.Count;
                    }
                    return (this.Columns.Count + 1);
                }
            }

            // PROPERTY:: SelectedIndices
            internal virtual ArrayList SelectedIndices
            {
                get
                {
                    cachedSelectedIndices = new ArrayList();
                    for (int i = 0; i < Rows.Count; i++)
                    {
                        // Retrieve the reference to the checkbox
                        CheckBox cb = (CheckBox)Rows[i].FindControl(InputCheckBoxField.CheckBoxID);
                        if (cb == null)
                            return cachedSelectedIndices;
                        if (cb.Checked)
                            cachedSelectedIndices.Add(i);
                    }
                    return cachedSelectedIndices;
                }
            }

            /// <summary>
            /// Gets the selected indices when selected by checkBox column
            /// </summary>
            /// <returns></returns>
            public virtual int[] GetSelectedIndices()
            {
                return (int[])SelectedIndices.ToArray(typeof(int));
            }

            #endregion

            #region Filter Region

            /// <summary>
            /// Gets the FilterBox TextBox used to filter the GridView
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"),
           DefaultValue(""), Description("Gets the FilterBox TextBox used to filter the GridView")]
            public TextBox FilterBox
            {
                get
                {
                    return this.searchBox;
                }
            }

            /// <summary>
            /// Gets/Sets the Filtered State of the GridView
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"),
           DefaultValue(false), Description("Gets/Sets the Filtered State of the GridView")]
            public bool IsFiltered
            {
                get
                {
                    return this.isFiltered;
                }
                set
                {
                    this.isFiltered = value;
                }
            }

            /// <summary>
            /// Gets/Sets the CSS class for the FilterBox
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"),
           DefaultValue(""), Description("Gets/Sets the CSS class for the FilterBox")]
            public string CSSFilterbox
            {
                get
                {
                    string cssFilterbox = string.Empty;
                    object obj = ViewState["CSSFilteredbox"];
                    if (obj != null)
                    {
                        cssFilterbox = obj.ToString();
                    }
                    return cssFilterbox;
                }
                set
                {
                    ViewState["CSSFilteredbox"] = value;
                }
            }
            
            /// <summary>
            /// Gets/Sets the SkinID for the FilterBox
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"),
           DefaultValue(""), Description("Gets/Sets the SkinID for the FilterBox")]
            public string SkinIDFilterbox
            {
                get
                {
                    string skinIDFilterbox = string.Empty;
                    object obj = ViewState["SkinIDFilterbox"];
                    if (obj != null)
                    {
                        skinIDFilterbox = obj.ToString();
                    }
                    return skinIDFilterbox;
                }
                set
                {
                    ViewState["SkinIDFilterbox"] = value;
                }
            }

            /// <summary>
            /// Gets the Style of the FilterBox
            /// </summary>
            [Category("Extended GridView Properties")]
            [PersistenceMode(PersistenceMode.InnerProperty)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
            [Description("Gets the Style of the FilterBox")]
            public virtual TableItemStyle FilterStyle
            {
                get
                {
                    if (filterStyle == null)
                        filterStyle = new TableItemStyle();

                    if (IsTrackingViewState)
                        ((IStateManager)filterStyle).TrackViewState();

                    return filterStyle;
                }
            }

            // Define a new event for the filter command event
            private static readonly object EventFilterCommand = new object();

            /// <summary>
            /// Filter Event used to add/remove delegate instances
            /// </summary>
            [Category("Extended GridView Properties")]
            public event FilterCommandEventHandler FilterCommand
            {
                add
                {
                    base.Events.AddHandler(xGrid.EventFilterCommand, value);
                }
                remove
                {
                    base.Events.RemoveHandler(xGrid.EventFilterCommand, value);
                }
            }

            protected virtual void OnFilterCommand(FilterCommandEventArgs e)
            {
                FilterCommandEventHandler handler1 = (FilterCommandEventHandler)base.Events[xGrid.EventFilterCommand];

                // Reset selection in GridView
                this.SelectedIndex = -1;

                if (handler1 != null)
                {
                    handler1(this, e);
                }
            }

            /// <summary>
            ///     Creates a custom header to be added
            /// </summary>
            private void CreateCustomHeader()
            {
                //gvRowSearch = new GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal);
                TableCell tableCell = new TableHeaderCell();
                tableCell.HorizontalAlign = HorizontalAlign.Right;
                tableCell.ColumnSpan = this.ActualColumns;
                tableCell.MergeStyle(this.FilterStyle);

                // Add the search textbox
                searchBox = new TextBox();
                tableCell.Controls.Add(searchBox);
                searchBox.ID = "txtSearch";
                searchBox.CssClass = CSSFilterbox;
                searchBox.SkinID = SkinIDFilterbox;

                searchBox.AutoPostBack = true;
                searchBox.TextChanged += new EventHandler(HandleFilterCommand);

                gvRowSearch.Cells.Add(tableCell);
            }

            // P6
            /// <summary>
            /// Handles the Filter Textbox TextChanged event
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void HandleFilterCommand(object sender, EventArgs e)
            {
                FilterCommandEventArgs filterArgs = new FilterCommandEventArgs();
                filterArgs.FilterExpression = this.searchBox.Text;

                OnFilterCommand(filterArgs);
            }

            /// <summary>
            ///     Adds the custom header to the GridView's table
            /// </summary>
            private void AddCustomHeader()
            {
                Table tbl = (Table)this.Controls[0];
                tbl.Rows.AddAt(0, this.gvRowSearch);
            }
            #endregion

            #region Single Row Click Region
            /// <summary>
            /// Specifies whether to enable Single Click Event or not
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"), DefaultValue(false), Description("Specifies whether to enable Double Click Event or not")]
            public bool EnableRowClick
            {
                get
                {
                    return this.enableRowClick;
                }
                set
                {
                    this.enableRowClick = value;
                }
            }

            // Define a new event called RowClick
            private static readonly object EventRowClick = new object();

            /// <summary>
            /// RowClick Event used to add/remove delegate instances
            /// </summary>
            [Category("Extended GridView Properties")]
            public event RowClickEventHandler RowClick
            {
                add
                {
                    this.Events.AddHandler(xGrid.EventRowClick, value);
                }
                remove
                {
                    this.Events.RemoveHandler(xGrid.EventRowClick, value);
                }
            }
            protected virtual void OnRowClick(RowClickEventArgs e)
            {
                // Set a selection in the Grid
                this.SelectedIndex = e.GridViewRow.RowIndex;

                RowClickEventHandler handler1 = (RowClickEventHandler)base.Events[xGrid.EventRowClick];
                if (handler1 != null)
                {
                    handler1(this, e);
                }
            }
            #endregion

            #region Context Menu
            /// <summary>
            /// Gets/Sets the ContextMenu ControlID used on the host page 
            /// to be referenced inside the Grid for each Row.
            /// </summary>
            [Category("Extended GridView Properties")]
            [Description("Specifies the ID of the ContextMenu used on the ASPX page")]
            [IDReferenceProperty(typeof(CustomControls.ContextMenuScope.ContextMenu))]
            [TypeConverter(typeof(CustomControls.ContextMenuScope.ContextMenuIDConvertor))]
            [Browsable(true)]
            public string ContextMenuID
            {
                get
                {
                    string contextMenuID = string.Empty;
                    object obj = this.ViewState["ContextMenuID"];
                    if (obj != null)
                    {
                        contextMenuID = obj.ToString();
                    }
                    
                    // Find the context menu
                    ContextMenu contextMenu = (ContextMenu)Utility.FindControl(this.Parent, contextMenuID);
                    if (contextMenu != null)
                        return contextMenu.ClientID;

                    return null;
                }
                set
                {
                    this.ViewState["ContextMenuID"] = value;
                }
            }
            
            /// <summary>
            /// Gets the GridViewRow where the ContextMenu was right-clicked.
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"),
            Description("Returns right clicked row index.")]
            public GridViewRow RightClickedRow
            {
                get
                {
                    // If ContextMenu is enabled
                    if (!string.IsNullOrEmpty(ContextMenuID))
                    {
                        // rowID is retrieved from the current request's form fields
                        int rowID = Convert.ToInt32(HttpContext.Current.Request.Form["__ROWID"].ToString());
                        return this.Rows[rowID];
                    }

                    return null;
                }
            }
            #endregion

            #region Double Row Click Region
            /// <summary>
            /// Specifies whether to enable Double Click Event or not
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"), DefaultValue(false), Description("Specifies whether to enable Double Click Event or not")]
            public bool EnableRowDoubleClick
            {
                get
                {
                    return this.enableRowDoubleClick;
                }
                set
                {
                    this.enableRowDoubleClick = value;
                }
            }

            // Define a new event for the double click event
            private static readonly object EventRowDoubleClick = new object();

            /// <summary>
            /// RowDoubleClick Event used to add/remove delegate instances
            /// </summary>
            [Category("Extended GridView Properties")]
            public event RowDoubleClickEventHandler RowDoubleClick
            {
                add
                {
                    base.Events.AddHandler(xGrid.EventRowDoubleClick, value);
                }
                remove
                {
                    base.Events.RemoveHandler(xGrid.EventRowDoubleClick, value);
                }
            }
            protected virtual void OnRowDoubleClick(RowDoubleClickEventArgs e)
            {
                // Set a selection in the Grid
                this.SelectedIndex = e.GridViewRow.RowIndex;

                RowDoubleClickEventHandler handler1 = (RowDoubleClickEventHandler)base.Events[xGrid.EventRowDoubleClick];
                if (handler1 != null)
                {
                    handler1(this, e);
                }
            }
            #endregion

            #region Single/Double Common Region
            /// <summary>
            /// Gets/Sets the color of mouse over the GridViewRow
            /// </summary>
            [Bindable(true), Category("Extended GridView Properties"), DefaultValue(typeof(Color), ""), Description("Mouse over color on a gridviewrow")]
            public Color MouseOverColor
            {
                get
                {
                    Color mouseOverColor = Color.Transparent;
                    object obj = ViewState["MouseOverColor"];
                    if (obj != null)
                    {
                        mouseOverColor = (Color)obj;
                    }
                    return mouseOverColor;
                }
                set
                {
                    ViewState["MouseOverColor"] = value;
                }
            }
            #endregion

            #region GridView Events
            // Step 1 - P1
            protected override void OnInit(EventArgs e)
            {
                // To save some properties in ControlState
                Page.RegisterRequiresControlState(this);

                base.OnInit(e);

                // Create cusotm header
                if (IsFiltered)
                    CreateCustomHeader();
            }

            // Step 3 - P4
            protected override void OnPagePreLoad(object sender, EventArgs e)
            {
                base.OnPagePreLoad(sender, e);
            }

            // Step 4 - P5
            protected override void OnLoad(EventArgs e)
            {
                // Call base Load
                base.OnLoad(e);

                // Include the GridViewJs.js, which includes all Js related functions
                Type t = this.GetType();
                string url = Page.ClientScript.GetWebResourceUrl(t, GRIDVIEW_JS);

                if (!Page.ClientScript.IsClientScriptIncludeRegistered(t, GRIDVIEW_JS))
                    Page.ClientScript.RegisterClientScriptInclude(t, GRIDVIEW_JS, url);
            }

            // Step 5 - P3
            protected override void CreateChildControls()
            {
                base.CreateChildControls();
            }

            // Step 5-2
            protected override Table CreateChildTable()
            {
                return base.CreateChildTable();
            }

            // Step 5-1
            protected override System.Collections.ICollection CreateColumns(PagedDataSource dataSource, bool useDataSource)
            {
                // Let the GridView create the default set of columns
                ICollection columnList = base.CreateColumns(dataSource, useDataSource);

                if (!AutoGenerateCheckBoxColumn)
                    return columnList;

                // Add a checkbox column if required
                ArrayList extendedColumnList = AddCheckBoxColumn(columnList);
                return extendedColumnList;
            }

            // Step 5-3
            protected override GridViewRow CreateRow(int rowIndex, int dataSourceIndex, DataControlRowType rowType, DataControlRowState rowState)
            {
                return base.CreateRow(rowIndex, dataSourceIndex, rowType, rowState);
            }

            // Step 5-4
            protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
            {
                base.InitializeRow(row, fields);
            }

            // Step 5-5
            // Row has been created with no data, so we get the Header row and add the image
            // asc or desc depending on the state of the Grid SortExpression itself.
            protected override void OnRowCreated(GridViewRowEventArgs e)
            {
                // Call base RowCreated
                base.OnRowCreated(e);

                // Handle Header Rows only.
                if (e.Row != null && e.Row.RowType == DataControlRowType.Header)
                {
                    foreach (TableCell tableCell in e.Row.Cells)
                    {
                        if (tableCell.HasControls())
                        {
                            LinkButton sortLink = tableCell.Controls[0] as LinkButton;

                            // If the Sorting is enabled
                            if (sortLink != null)
                            {
                                // Image to add beside each HeaderText
                                System.Web.UI.WebControls.Image sortImage = new System.Web.UI.WebControls.Image();
                                sortImage.ImageUrl = "default.gif";

                                if (this.SortExpression == sortLink.CommandArgument)
                                {
                                    // ASC image if we have Ascending sorting
                                    if (this.SortDirection == SortDirection.Ascending)
                                    {
                                        //string ascImage = AscImage;//"~/App_Images/asc.gif";
                                        sortImage.ImageUrl = AscImage;
                                    }
                                    else
                                    {
                                        // DESC image if we have Descending sorting
                                        //string descImage = "~/App_Images/desc.gif";
                                        sortImage.ImageUrl = DescImage;
                                    }// End else
                                }// End if

                                // Add the image (asc or desc) to the Header cell
                                if (!sortImage.ImageUrl.Equals("default.gif"))
                                {
                                    tableCell.Controls.Add(new LiteralControl("&nbsp;"));
                                    tableCell.Controls.Add(sortImage);
                                    tableCell.Controls.Add(new LiteralControl("&nbsp;"));
                                }// End if
                            }// End if
                        }// End if
                    }// End For

                    // Add custom header
                    if (IsFiltered)
                        AddCustomHeader();

                }// End if
            }

            // Step 5-6
            protected override void OnRowDataBound(GridViewRowEventArgs e)
            {
                base.OnRowDataBound(e);
            }

            // Step 6
            protected override void OnDataBound(EventArgs e)
            {
                // Call base OnDataBound.
                base.OnDataBound(e);
            }

            // Step 8 - P8
            protected override object SaveControlState()
            {
                object[] controlState = new object[6];
                controlState[0] = base.SaveControlState();
                controlState[1] = this.isFiltered;
                controlState[2] = this.autoGenerateCheckBoxColumn;
                controlState[3] = this.checkBoxColumnIndex;
                controlState[4] = this.enableRowClick;
                controlState[5] = this.enableRowDoubleClick;

                return controlState;
            }

            // P2
            protected override void LoadControlState(object savedState)
            {
                object[] loadControl = (object[])savedState;
                base.LoadControlState(loadControl[0]);
                this.isFiltered = (bool)loadControl[1];
                this.autoGenerateCheckBoxColumn = (bool)loadControl[2];
                this.checkBoxColumnIndex = (int)loadControl[3];
                this.enableRowClick = (bool)loadControl[4];
                this.enableRowDoubleClick = (bool)loadControl[5];
            }
    
            // Step 7 - P7
            // Override the PreRender to add some JS script 
            protected override void OnPreRender(EventArgs e)
            {
                // Call base PreRender
                base.OnPreRender(e);

                // If Grid is set to be filtered, then add this JS
                if (IsFiltered)
                {
                    // Hold the script key
                    string scriptKey = "isFiltered" + this.UniqueID;
                    if (!Page.ClientScript.IsStartupScriptRegistered(scriptKey))
                    {
                        StringBuilder script = new StringBuilder();

                        script.Append("// Set focus on the search textbox");
                        script.Append("\r\n");
                        script.Append("document.getElementById('" + searchBox.ClientID + "').focus();");
                        script.Append("\r\n");
                        script.Append("\r\n");

                        Page.ClientScript.RegisterStartupScript(typeof(xGrid), scriptKey, script.ToString(), true);
                    }
                }

                // Add a hidden field to hold the Row that is right-clicked by the context menu.
                // if ContextMenuID is not null, means HiddenField should be added
                if (!string.IsNullOrEmpty(ContextMenuID))
                {
                    Page.ClientScript.RegisterHiddenField("__ROWID", "-1");
                }
            }

            // Step 9 - P9
            protected override void Render(HtmlTextWriter writer)
            {
                // Callbase Render to render the contents
                base.Render(writer);
            }

            // Step 9-1  P9-1
            // Customize each row to distinguish the RowClicked event
            protected override void PrepareControlHierarchy()
            {
                base.PrepareControlHierarchy();

                // Hold the row/double click row
                string argsData = string.Empty;

                // if the EnableRowClick is true, then customize the rows
                if (EnableRowClick)
                {
                    for (int i = 0; i < (base.Rows.Count); i++)
                    {
                        argsData = "rc" + base.Rows[i].RowIndex.ToString();
                        base.Rows[i].Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(this, argsData));
                    }
                }

                // if the EnableRowDoubleClick is true, then customize the rows
                if (EnableRowDoubleClick)
                {
                    for (int i = 0; i < (base.Rows.Count); i++)
                    {
                        argsData = "rdc" + base.Rows[i].RowIndex.ToString();
                        this.Rows[i].Attributes.Add("onDblClick", Page.ClientScript.GetPostBackEventReference(this, argsData));
                    }
                }

                // If any of the two properties is enabled add the following
                // customization features.
                if (EnableRowClick || EnableRowDoubleClick)
                {
                    for (int i = 0; i < (base.Rows.Count); i++)
                    {
                        this.Rows[i].Attributes.Add("onMouseOver", "this.style.cursor='hand';__MouseOverRow(this, '" + ColorTranslator.ToHtml(MouseOverColor) + "');");
                        this.Rows[i].Attributes.Add("onMouseOut", "__MouseOutRow(this);");
                    }
                }

                // if ContextMenuID is not null, means ContextMenu should be enabled.
                if (!string.IsNullOrEmpty(ContextMenuID))
                {
                    for (int i = 0; i < (base.Rows.Count); i++)
                    {
                        //argsData = "rc" + base.Rows[i].RowIndex.ToString();
                        this.Rows[i].Attributes.Add("oncontextmenu", "return __showContextMenu(" + ContextMenuID + "_Root, " + base.Rows[i].RowIndex + ");");
                    }
                }

                // Add empty columns so that the Grid's Height is kept the same
                // this is mainly shown in the last page when we have paging and
                // the records shown are less then the page size. Empty cells
                // now appears keeping the height of the Grid the same.
                if (this.enableHeightResolver)
                {
                    GridViewRow gvRow = null;
                    for (int rows = this.Rows.Count; rows < this.PageSize; rows++)
                    {
                        // Create a dummy Row of type DataRow and Normal State
                        gvRow = new GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal);

                        for (int columns = 0; columns < this.ActualColumns; columns++)
                        {
                            // Add columns with &nbsp; (space) to take a height
                            TableCell tableCell = new TableCell();
                            tableCell.Controls.Add(new LiteralControl("&nbsp;"));
                            gvRow.Controls.Add(tableCell);
                        }

                        //Inserts the rows right above the footer row. 
                        //Remove the "- 1" if you are not using a footer.
                        if (this.Rows.Count > 0)
                        {
                            this.Controls[0].Controls.AddAt(this.Controls[0].Controls.Count - 1, gvRow);
                        }
                    }
                }

                // Apply checkbox styles
                ApplyCheckBoxStyle();
            }

            /// <summary>
            ///     Used to apply the Selected Style on rows selected
            /// </summary>
            private void ApplyCheckBoxStyle()
            {
                if (AutoGenerateCheckBoxColumn)
                {
                    // Adjust each data row
                    foreach (GridViewRow r in Rows)
                    {
                        // Get the appropriate style object for the row
                        TableItemStyle style = GetRowStyleFromState(r.RowState);

                        // Retrieve the reference to the checkbox
                        CheckBox cb = (CheckBox)r.FindControl(InputCheckBoxField.CheckBoxID);

                        // Build the ID of the checkbox in the header
                        string headerCheckBoxID = String.Format(CheckBoxColumHeaderID, ClientID);

                        // Add script code to enable selection
                        cb.Attributes["onclick"] = String.Format("ApplyStyle(this, '{0}', '{1}', '{2}', '{3}', '{4}', '{5}')",
                                ColorTranslator.ToHtml(SelectedRowStyle.ForeColor),
                                ColorTranslator.ToHtml(SelectedRowStyle.BackColor),
                                ColorTranslator.ToHtml(style.ForeColor),
                                ColorTranslator.ToHtml(style.BackColor),
                                (style.Font.Bold ? 700 : 400),
                                headerCheckBoxID);

                        // Update the style of the checkbox if checked
                        if (cb.Checked)
                        {
                            r.BackColor = SelectedRowStyle.BackColor;
                            r.ForeColor = SelectedRowStyle.ForeColor;
                            r.Font.Bold = SelectedRowStyle.Font.Bold;
                        }
                        else
                        {
                            r.BackColor = style.BackColor;
                            r.ForeColor = style.ForeColor;
                            r.Font.Bold = style.Font.Bold;
                        }
                    }
                }
            }

            // Step 9-2 P9-2
            protected override void RenderContents(HtmlTextWriter writer)
            {
                base.RenderContents(writer);
            }

            // Step 10 P10
            protected override void OnUnload(EventArgs e)
            {
                base.OnUnload(e);
            }

            // Override the RaisePostBackEvent to handle the Row Click
            protected override void RaisePostBackEvent(string eventArgument)
            {
                if (eventArgument.StartsWith("rc"))
                {
                    int rowClicked = Int32.Parse(eventArgument.Substring(2));
                    RowClickEventArgs rowClickEventArgs = new RowClickEventArgs(this.Rows[rowClicked]);
                    OnRowClick(rowClickEventArgs);
                }
                else
                    if (eventArgument.StartsWith("rdc"))
                    {
                        int rowDoubleClicked = Int32.Parse(eventArgument.Substring(3));
                        RowDoubleClickEventArgs rowDoubleClickEventArgs = new RowDoubleClickEventArgs(this.Rows[rowDoubleClicked]);
                        OnRowDoubleClick(rowDoubleClickEventArgs);
                    }
                    else
                        {
                            base.RaisePostBackEvent(eventArgument);
                        }
            }

            // Sets the Grid in the Insert State
            public void ChangeInsertMode(bool currentView)
            {
                this.ShowFooter = currentView;
            }
            #endregion

            #region Fix Datasource on Paging, Sorting, RowEditing, RowDeleting, RowUpdating, IndexChanging, RowCancelEdit
            protected override void OnSorting(GridViewSortEventArgs e)
            {
                // Call base sorting
                base.OnSorting(e);

                CallFilterCommandHandler();
            }

            protected override void OnRowCancelingEdit(GridViewCancelEditEventArgs e)
            {
                // Call base Row Cancel Editing
                base.OnRowCancelingEdit(e);

                CallFilterCommandHandler();
            }
            protected override void OnRowEditing(GridViewEditEventArgs e)
            {
                base.OnRowEditing(e);

                CallFilterCommandHandler();
            }

            protected override void OnRowDeleting(GridViewDeleteEventArgs e)
            {
                // Call base Deleting
                base.OnRowDeleting(e);

                CallFilterCommandHandler();
            }

            protected override void OnRowUpdating(GridViewUpdateEventArgs e)
            {
                // Call base Updating
                base.OnRowUpdating(e);

                CallFilterCommandHandler();
            }

            protected override void OnSelectedIndexChanging(GridViewSelectEventArgs e)
            {
                // Call base Index Changing
                base.OnSelectedIndexChanging(e);

                CallFilterCommandHandler();
            }

            protected override void OnPageIndexChanging(GridViewPageEventArgs e)
            {
                // Call base Page Index Changing
                base.OnPageIndexChanging(e);

                CallFilterCommandHandler();
            }

            /// <summary>
            ///     Used by the above overriden methods to handle data correctly
            /// </summary>
            private void CallFilterCommandHandler()
            {
                if (IsFiltered)
                {
                    // Reset selection in GridView
                    this.SelectedIndex = -1;


                    // Fire the OnFilterCommand to set the right data source.
                    FilterCommandEventArgs filterArgs = new FilterCommandEventArgs(this.searchBox.Text);
                    OnFilterCommand(filterArgs);
                }
            }
            #endregion
        }

        #region EventArgs Classes
        #region RowClick Event Args Class

        /// <summary>
        /// RowClickEventArgs Class, specifies a single property called
        /// GridViewRow which is the Row where the Single Click occured.
        /// </summary>
        public class RowClickEventArgs : EventArgs
        {
            private GridViewRow gridViewRow;
            public GridViewRow GridViewRow
            {
                get { return gridViewRow; }
                set { gridViewRow = value; }
            }

            public RowClickEventArgs()
            {
            }

            public RowClickEventArgs(GridViewRow gridViewRow)
            {
                this.gridViewRow = gridViewRow;
            }
        }
        #endregion

        #region RowDoubleClick Event Args Class
        
        /// <summary>
        /// RowDoubleClickEventArgs Class, specifies a single property called
        /// GridViewRow which is the Row where the Double Click occured.
        /// </summary>
        public class RowDoubleClickEventArgs : EventArgs
        {
            private GridViewRow gridViewRow;
            public GridViewRow GridViewRow
            {
                get { return gridViewRow; }
                set { gridViewRow = value; }
            }

            public RowDoubleClickEventArgs()
            {
            }

            public RowDoubleClickEventArgs(GridViewRow gridViewRow)
            {
                this.gridViewRow = gridViewRow;
            }
        }
        #endregion

        #region FilterCommand Event Args Class
        
        /// <summary>
        /// FilterCommandEventArgs Class, specifies a single property called
        /// FilterExpression that contains the expression to filter the
        /// GridView on.
        /// </summary>
        public class FilterCommandEventArgs : EventArgs
        {
            private string filterExpression;
            public string FilterExpression
            {
                get { return filterExpression; }
                set { filterExpression = value; }
            }

            public FilterCommandEventArgs()
            {
            }

            public FilterCommandEventArgs(string filterExpression)
            {
                this.filterExpression = filterExpression;
            }
        }
        #endregion
        #endregion

        #region Delegates
        /// <summary>
        /// FilterCommandEventHandler Delegate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void FilterCommandEventHandler(object sender, FilterCommandEventArgs e);
        
        /// <summary>
        /// RowClickEventHandler Delegate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void RowClickEventHandler(object sender, RowClickEventArgs e);
        
        /// <summary>
        /// RowDoubleClickEventHandler Delegate
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void RowDoubleClickEventHandler(object sender, RowDoubleClickEventArgs e);
        #endregion
    }
}